﻿using CloudStorage_Backend.exception;
using CloudStorage_Backend.model;
using CloudStorage_Backend.utils;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace CloudStorage_Backend.service
{
    public class FileService
    {
        
        public void UploadFile(IFormFile inputStream, FileChunkInfoVO info, string userName, string webRootPath) {
            FileHelper.ValidateRequest(info);

            string extension = info.FlowFileName.Substring(info.FlowFileName.IndexOf("."));
            string filePathDownloading = FileHelper.ProcessTempPath(info.FlowIdentifier, info.FlowChunkNumber, userName, webRootPath, true)
                                      + extension;

            using (FileStream fileStream = File.Create(filePathDownloading))
             {
                 long transferred = inputStream.Length;
                 inputStream.CopyTo(fileStream);
                 fileStream.Flush();
                 FileHelper.CheckFileChunk(info, Convert.ToInt32(transferred));
             }
            string filePathChunk = FileHelper.ProcessTempPath(info.FlowIdentifier, info.FlowChunkNumber, userName, webRootPath)
                                   +extension;

            string finalPath = "";
            try
            {
                // 下载完成重命名
                File.Move(filePathDownloading, filePathChunk);
                if(info.FlowChunkNumber == info.FlowTotalChunks)
                {
                    // finalPath 有拓展名

                    // 如果该文件已经存在，对该文件名加(i) relativePath就是fileName, 例如1.jpg
                    finalPath = FileHelper.ProcessFinalPath(info.DestinationPath, info.FlowRelativePath, userName, webRootPath);
                    int i = 1;
                    while(File.Exists(finalPath))
                    {
                        string exten = info.FlowFileName.Substring(info.FlowFileName.IndexOf("."));
                        string newName = info.FlowFileName.Substring(0, info.FlowFileName.IndexOf(".")) + $"({i++})" + exten;
                        finalPath = FileHelper.ProcessFinalPath(info.DestinationPath, newName, userName, webRootPath);
                    }
                    using (BufferedStream bufferedStream = new BufferedStream(File.Create(finalPath)))
                    {
                        for (int chunk = 1; chunk <= info.FlowTotalChunks; chunk++)
                        {
                            string chunkFilePath = FileHelper.ProcessTempPath(info.FlowIdentifier, chunk, userName, webRootPath)
                                                   + extension;
                            using (BufferedStream input = new BufferedStream(File.OpenRead(chunkFilePath)))
                            {
                                input.CopyTo(bufferedStream);
                                bufferedStream.Flush();
                            }
                            File.Delete(chunkFilePath);
                            if(chunk == info.FlowTotalChunks)
                            {
                                Directory.Delete(Directory.GetParent(chunkFilePath).FullName, true);
                            }
                        }
                    }
                }

            } catch (Exception e)
            {
                //若下载失败，应该删除已经下载的
                if(File.Exists(finalPath))
                {
                    File.Delete(finalPath);
                }
                if(info.FlowChunkNumber == info.FlowTotalChunks)
                {
                    string chunkFilePath = FileHelper.ProcessTempPath(info.FlowIdentifier, info.FlowChunkNumber, userName, webRootPath)
                                          +extension;
                    Directory.Delete(Directory.GetParent(chunkFilePath).FullName, true);
                }
                throw e;
            }

        }

        public List<FileInfoVO> GetFileList(string path, string userName, string webRootPath)
        {
            string filePath = FileHelper.ProcessPath(path, userName, webRootPath);  // ..../userName/...
            if (path == "" || path == "/")
            {
                List<FileInfoVO> list = new List<FileInfoVO>();
                if(!Directory.Exists(filePath))
                {
                    filePath += "/root";   // ..../userName/root
                    Directory.CreateDirectory(filePath);
                    list.Add(FileHelper.BuildDirectory("我的资源", "/root"));
                    return list;
                }
            }

            FileHelper.FileListCheck(filePath);
            string root = webRootPath + $"/{userName}";
            List<FileInfoVO> result = new List<FileInfoVO>();
            // 遍历子文件
            List<string> childPaths = Directory.GetFileSystemEntries(filePath).ToList();
            childPaths.Where(p => p != filePath);
            childPaths.ForEach(p => {
                if(Directory.Exists(p)) {
                    DirectoryInfo info = new DirectoryInfo(p);
                    result.Add(new FileInfoVO {
                    FileName = info.Name,
                    FilePath = p.Substring(p.IndexOf(root) + root.Length),
                    Size = 0,
                    LastModified = Directory.GetCreationTime(p),
                    IsDirectory = true,
                    IsPicture = false
                });
                } else {
                    FileInfo info = new FileInfo(p);
                    result.Add(new FileInfoVO {
                    FileName = info.Name,
                    FilePath = p.Substring(p.IndexOf(root) + root.Length),
                    Size = info.Length,
                    LastModified = File.GetLastWriteTime(p),
                    IsDirectory = false,
                    IsPicture = FileHelper.CheckIsPicture(info.Name)
                });
                }
            });
            return result;
        }

        public List<FileInfoVO> SearchFile(string userName, string webRootPath, string path, string keyword)
        {
            string root = webRootPath + $"/{userName}";
            string filePath = FileHelper.ProcessPath(path, userName, webRootPath);  // .../userName/..
            List<FileInfoVO> result = new List<FileInfoVO>();
            List<string> childPaths = Directory.GetFileSystemEntries(filePath).ToList();
            childPaths.Where(p => p != filePath);
            childPaths.ForEach(p => {
                if (Directory.Exists(p))
                {
                    DirectoryInfo info = new DirectoryInfo(p);
                    if(info.Name.Contains(keyword))
                    {
                        result.Add(new FileInfoVO
                        {
                            FileName = info.Name,
                            FilePath = p.Substring(p.IndexOf(root) + root.Length),
                            Size = 0,
                            LastModified = Directory.GetCreationTime(p),
                            IsDirectory = true,
                            IsPicture = false
                        });
                    }
                }
                else
                {
                    FileInfo info = new FileInfo(p);
                    if(info.Name.Contains(keyword))
                    {
                        result.Add(new FileInfoVO
                        {
                            FileName = info.Name,
                            FilePath = p.Substring(p.IndexOf(root) + root.Length),
                            Size = info.Length,
                            LastModified = File.GetLastWriteTime(p),
                            IsDirectory = false,
                            IsPicture = FileHelper.CheckIsPicture(info.Name)
                        });
                    }
                }
            });
            return result;
        }

        public void CreateFolder(string path, string userName, string webRootPath, bool process = true)
        {
            string dirsPath = path;
            if(process)
            {
                dirsPath = FileHelper.ProcessPath(path, userName, webRootPath);
            }
            if(!Directory.Exists(dirsPath) && !File.Exists(dirsPath))
            {
                Directory.CreateDirectory(dirsPath);
            } else
            {
                int i = 1;
                string newPath = dirsPath;
                while(Directory.Exists(newPath))
                {
                    newPath = $"{dirsPath}({i++})";
                }
                Directory.CreateDirectory(newPath);
            }
        }

        public void DeleteFiles(string userName, string webRootPath, List<string> paths)
        {
            paths.ForEach(p =>
            {
                string filePath = FileHelper.ProcessPath(p.Substring(1), userName, webRootPath);
                if(!Directory.Exists(filePath) && !File.Exists(filePath))
                {
                    throw new FileException(FileExceptionMsg.FILE_NOT_FOUND);
                }

                if(Directory.Exists(filePath))
                {
                    Directory.Delete(filePath, true);
                } else
                {
                    File.Delete(filePath);
                }
            });
        }

        public void CopyFiles(string userName, string webRootPath, string destinationPath, List<string> filePaths)
        {
            string destination = FileHelper.ProcessPath(destinationPath, userName, webRootPath);
            filePaths.ForEach((p) =>
            {
                string filePath = FileHelper.ProcessPath(p.Substring(1), userName, webRootPath);
                if (!Directory.Exists(filePath) && !File.Exists(filePath))
                {
                    throw new FileException(FileExceptionMsg.FILE_NOT_FOUND);
                }

                string fileName = p.Substring(p.LastIndexOf(@"\")+1);
                using (BufferedStream input = new BufferedStream(File.Create(Path.Combine(destination, fileName))))
                {
                    using (BufferedStream output = new BufferedStream(File.OpenRead(filePath)))
                    {
                        output.CopyTo(input);
                        input.Flush();
                    }
                }
            });
        }

        public void CutFiles(string userName, string webRootPath, string destinationPath, List<string> filePaths)
        {
            CopyFiles(userName, webRootPath, destinationPath, filePaths);
            DeleteFiles(userName, webRootPath, filePaths);
        }

        public string ReadTextFile(string userName, string webRootPath, string filePath, int pageNum, int pageSize=1000)
        {
            string path = FileHelper.ProcessPath(filePath, userName, webRootPath);
            if(!File.Exists(path))
            {
                throw new FileException(FileExceptionMsg.FILE_NOT_FOUND);
            }
            FileInfo info = new FileInfo(path);
            if(pageNum < 0)
            {
                if(info.Length > 1024*1024)
                {
                    throw new FileException(FileExceptionMsg.FILE_TOO_LARGE);
                }
                using(StreamReader reader = new StreamReader(info.OpenRead()))
                {
                    return reader.ReadToEnd();
                }
            }
            using (BufferedStream stream = new BufferedStream(info.OpenRead()))
            {
                long length = stream.Length;
                if (pageSize * (pageNum + 1) > length && pageNum * pageSize < length)
                {
                    byte[] bytes = new byte[length - pageNum * pageSize];
                    stream.Write(bytes, pageSize * pageNum, bytes.Length);
                    return Convert.ToBase64String(bytes);
                } else
                {
                    byte[] bytes = new byte[pageSize];
                    stream.Write(bytes, pageSize * pageNum, pageSize);
                    return Convert.ToBase64String(bytes);
                }
            }
        }

        public string ReadPict(string userName, string webRootPath, string pictPath)
        {
            return FileHelper.ProcessPath(pictPath, userName, webRootPath);
        }
    }
}
